/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Actor.cpp
*
*	Comments	-	See Actor.h
*
**************************************************************************************/
#include "../Include/Actor.h"
#include "../Include/Engine.h"
#include "../Include/Scene.h"
#include "../Include/GlobalGraphicsStrings.h"
#include "../Include/EventRender.h"
#include "../Include/EventComponent.h"

namespace Pulse
{
	Actor::Actor( const ActorID id )
		: m_ID( id ), m_bEnabled( TRUE ), m_pParent( PSX_NULL ), m_pTransform( PSX_NULL ), m_pScene( PSX_NULL )
	{

	}

	Actor::~Actor( void )
	{
		
		RemoveAllComponents(); 
	}

	EErrorCode::Type Actor::Initialize( XMLElementPtr pElem )
	{
		m_pTransform = (ComponentTransform*)AddComponent( EComponent::TRANSFORM, pElem );

		return EErrorCode::OKAY;
	}

	void Actor::Update( FLOAT dt )
	{
		m_components.Update( dt );
		
		// NOTE: How about we move this after all the children has been updated?
		m_eventSystem.ProcessEvents();

		// Update children
		{
			Actor *pChild;

			for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
			{
				pChild = m_children[i];

				if ( pChild )
				{
					pChild->Update( dt );
				}
			}
		}
	}

	void Actor::Render( Scene *pScene, ComponentCamera *pCamera, RenderView *pView )
	{
		// Normally there's only 1 or 2 components out of many that does something to render
		// so fire an event instead of iterating through the component list.
		EventRender event;
		
		event.SetCamera( pCamera );
		event.SetRenderView( pView );
		event.SetScene( pScene );

		Render( &event );
	}

	BOOL Actor::IsEnabled( void ) const 
	{ 
		if ( !m_bEnabled )
			return FALSE;

		// Is this node really enabled?
		if ( m_pParent )
			return m_pParent->IsEnabled();
		else
			return TRUE;
	}

	void Actor::ToggleEnabled( void ) 
	{ 
		m_bEnabled = !m_bEnabled; 
	}

	IComponent * Actor::AddComponent( EComponent::Type type, XMLElementPtr pElem )
	{

		IComponent *pNewComponent = m_components.AddComponent( type, this, pElem );

		// Special components needs additional housekeeping
		if ( type == EComponent::CAMERA )
		{
			Scene *pScene = GetScene();
		}

		// Broadcast event
		EventPtr pEvent = new EventComponentAdd( pNewComponent );
		PostEvent( pEvent );

		return pNewComponent;
	}

	INDEX_T Actor::AttachChild( ActorPtr pChild )
	{

		// Remove any references from its original parent first
		if ( Actor * pParent = pChild->GetParent() )
		{
			pParent->DetachChild( pChild );
		}

		pChild->m_pParent = this;

		for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			if ( m_children[i].IsNull() )
			{
				m_children[i] = pChild;
				return i;
			}
		}

		m_children.PushBack( pChild );

		return m_children.GetSize() - 1;
	}

	void Actor::DetachChild( const Actor *pActor )
	{
		if ( pActor == PSX_NULL )
			return;

		for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			if ( (&*m_children[i]) == pActor )
			{
				m_children[i] = PSX_NULL;
				m_children.Remove( i );
				return;
			}
		}

	}

	ActorPtr Actor::GetChild( UINT index )
	{
		if ( index < m_children.GetSize() )
			return m_children[index];

		return PSX_NULL;
	}

	ActorPtr Actor::GetChild( const Actor *pActor )
	{
		for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			if ( &*m_children[i] == pActor )
			{
				return m_children[i];
			}
		}

		PSX_PushWarning( "Child actor is not in this child list." );
		return PSX_NULL;
	}

	void Actor::DetachAllChildren( void )
	{
		/*for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			ActorPtr pSpatial = m_children[i];

			if ( pSpatial )
				Destroy( pSpatial );

			m_children[i] = PSX_NULL;
		}*/

		while ( m_children.GetSize() )
		{
			ActorPtr pSpatial = m_children[0];

			if ( pSpatial )
				Destroy( pSpatial );

			// m_children[0] = PSX_NULL; // Destroy already handles the nulling of ActorPtr

			// This is now not necessary as the Destroy function
			//	 removes the item in the container
			//m_children[i] = PSX_NULL;
		}

		m_children.Clear();
	}

	void Actor::RemoveAllComponents( void )
	{
		m_components.ClearComponents();
	}

	void Actor::Destroy( Actor *pActor )
	{
		if ( pActor != this )
		{
			pActor->Destroy( pActor );
			return;
		}

		// Remove all children first before processing self
		DetachAllChildren();

		Actor *pParent = GetParent();

		if ( pParent != PSX_NULL )
		{
			// We need to get the smart pointer first before detach to maintain 
			// reference and inserted in the defered actor destroy list
			ActorPtr pSelfPtr = pParent->GetChild( this );

			// Check that this isn't the camera
			{
				ActorPtr actorCam = GetScene()->GetCamera();
				
				if ( actorCam.IsValid() && &*actorCam == this )
				{
					GetScene()->SetCamera( PSX_NULL );
				}
			}

			pParent->DetachChild( this );
			m_pParent = PSX_NULL;

			if ( pSelfPtr )
			{
				GetScene()->QueueActorDestroy( pSelfPtr );
			}
			else
			{
				PSX_PushError( "Actor on destroy self pointer is null." );
			}
		}
		else
		{
			// This is the root
			ActorPtr pSelfPtr = GetScene()->GetRootPtr();
			
			if ( pSelfPtr )
			{
				GetScene()->QueueActorDestroy( pSelfPtr );
			}
			else
			{
				PSX_PushError( "Actor on destroy self pointer is null." );
			}
		}
	}

	void Actor::Destroy( void )
	{
		Destroy( this );
	}

	void Actor::Destroy( IComponent *pComponent )
	{
		if ( pComponent == PSX_NULL )
		{
			PSX_PushError( "pComponent is null." );
			return;
		}

		// Queue component to be destroyed
		GetScene()->QueueComponentDestroy( pComponent );

		// Then post event to be broadcasted
		EventPtr pEvent = new EventComponentRemove( pComponent );
		PostEvent( pEvent );
	}

	void Actor::BindRenderParameters( void )
	{
		m_params.BindParameters();
	}

	void Actor::InvalidateTransforms( void )
	{
		// TODO: We can take advantage of invalidate flag coherency
		//	and simply skip and stop invalidating if we see that it 
		//	is already invalid.

		m_pTransform->InvalidateWorldTransform();

		Actor *pChild;

		for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			pChild = m_children[i];

			if ( pChild )
			{
				pChild->InvalidateTransforms();
			}
		}
	}

	void Actor::Render( EventRender *pEventRender )
	{
		ExecuteEvent( pEventRender );

		Actor *pChild;

		for ( SIZE_T i = 0; i < m_children.GetSize(); ++i )
		{
			pChild = m_children[i];

			if ( pChild )
			{
				pChild->Render( pEventRender );
			}
		}
	}

}